package com.allen.app.common.config.other;

import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.allen.app.common.config.aspect.AcpectCommonUtils;
import com.allen.app.common.utils.CheckObjectUtils;
import com.allen.app.common.utils.Jackson;
import com.allen.app.common.utils.ServletContextUtil;
import io.swagger.annotations.ApiOperation;
import java.util.Objects;
import java.util.Random;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

/**
 * Description: 接口耗时统计
 *
 * @author Limiaojun
 * @date 2019/9/5 12:15
 **/
@Aspect
@Component
@Slf4j
@Order(10)
public class ApiTimingInterceptor {

    private TimeInterval interval = new TimeInterval();
    private ThreadLocal<HttpServletRequest> threadLocal = new ThreadLocal<>();
    private JSONObject paramJson = new JSONObject();

    /**
     * 定义切入点，切入点为com.example.aop下的所有函数
     */
    @Pointcut("execution("
        + "public * com.*.*.api..*.*(..)) "
        + "|| execution(public * com.*.*.*.api..*.*(..))"
        + "")
    public void apiTimingInterceptor() {
    }

    @Before(value = "apiTimingInterceptor()")
    public void before(JoinPoint joinPoint) {
        MDC.put("logId", RandomUtil.randomString(11));
        paramJson.clear();
        ProceedingJoinPoint point = (ProceedingJoinPoint) joinPoint;
        coverJsonObj(point);
        try {
            threadLocal.set(ServletContextUtil.getRequest());
            if (CheckObjectUtils.isNull(threadLocal.get())) {
                log.info("{}:\t ,args: {}", getApiDescription(point), Jackson.json(paramJson));
            } else {
                log.info("{}:\t [{url: \"{}\t{}\",ip: \"{}\",args: {}}]",
                    getApiDescription(point),
                    threadLocal.get().getMethod(),
                    threadLocal.get().getRequestURI(),
                    ServletContextUtil.getIpAddress(),
                    Jackson.json(paramJson)
                );
            }

        } catch (Exception e) {
            log.error("{} exception: {}", getApiDescription(point), paramJson);
        }
        interval.start();
    }

    @After(value = "apiTimingInterceptor()")
    public void after(JoinPoint joinPoint) {
        ProceedingJoinPoint point = (ProceedingJoinPoint) joinPoint;
        try {
            log.info("{}:\t execute spend [{}]ms. url:{}", getApiDescription(point),
                interval.intervalMs(),
                Objects.isNull(threadLocal.get()) ? null : threadLocal.get().getRequestURI()
            );
        } catch (Exception e) {
            log.error("{} exception: {}", getApiDescription(point), e);
        }
    }

    /**
     * 获取api描述信息.
     *
     * @param point point
     * @return java.lang.String
     * @author Limiaojun
     * @date 2019/9/19 15:13
     */
    private String getApiDescription(ProceedingJoinPoint point) {
        String className = AcpectCommonUtils.getCompleteClassName(point);
        String methodName = AcpectCommonUtils.getMethodName(point);
        ApiOperation annotationClass = AcpectCommonUtils.getAnnotationClass(point, ApiOperation.class);
        StringBuilder apiDescription = new StringBuilder("");
        if (!CheckObjectUtils.isNull(annotationClass) && !CheckObjectUtils.isNull(annotationClass.value())) {
            apiDescription.append(annotationClass.value());
        } else {
            apiDescription.append(className).append(".").append(methodName);
        }
        return apiDescription.toString();
    }

    /**
     * 切面参数转Json.
     *
     * @param point point
     * @author Limiaojun
     * @date 2019/9/19 15:17
     */
    private void coverJsonObj(ProceedingJoinPoint point) {
        String[] parameterNames = AcpectCommonUtils.getParameterNames(point);
        Object[] args = AcpectCommonUtils.getArgs(point);
        for (int i = 0; i < parameterNames.length; i++) {
            //忽略response
            if(args[i] instanceof ServletRequest){
                continue;
            }
            if(args[i] instanceof ServletResponse){
                continue;
            }
            if(args[i] instanceof MultipartFile){
                continue;
            }
            paramJson.put(parameterNames[i], args[i]);
        }
    }
}